import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.swagger.annotations.ApiModelProperty;
import javafx.scene.control.Labeled;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.stream.Collectors;

/**
 * describe:
 *
 * @author huxingjie
 * @date 2020/10/29
 * @since 5.0
 */
@Data
public class LabelExpression implements Printable {
    @ApiModelProperty(value = "括号")
    private String brackets;
    //private Brackets brackets;

    @ApiModelProperty(value = "标签项", required = true)
    //@NotNull
    //@Valid
    private LabelFilter labelFilter;

    @ApiModelProperty(value = "操作符")
    private String labelOperator;
    //private LabelOperator labelOperator;

    @ApiModelProperty(value = "标签过滤表达式")
    private LabelExpression expression;

    public void beginBrackets() {
        this.brackets = Brackets.LEFT.toString();
    }

    public void endBrackets() {
        this.brackets = Brackets.RIGHT.toString();
    }

    public String getQuerySql(String filterField) {
        LabelExpression itrExp = this;
        StringBuilder sql = new StringBuilder();
        while (itrExp != null) {
            if (null != itrExp.brackets && itrExp.brackets.equals(Brackets.LEFT.oprator)) {
                sql.append(itrExp.brackets);
            }
            if (null != itrExp.labelFilter) {
                sql.append(filterField + "=term('" + itrExp.labelFilter.toString() + "')");
            }
            if (null != itrExp.brackets && itrExp.brackets.equals(Brackets.RIGHT.oprator)) {
                sql.append(itrExp.brackets);
            }
            if (null != itrExp.labelOperator) {
                sql.append(itrExp.labelOperator);
            }
            itrExp = itrExp.expression;
        }
        return sql.toString();
    }

    @Override
    public void print() {
        LabelExpression itrExp = this;
        do {
            if (null != itrExp.brackets && itrExp.brackets.equals(Brackets.LEFT.oprator)) {
                //itrExp.brackets.print();
                System.out.print(itrExp.brackets);
            }
            if (null != itrExp.labelFilter) {
                itrExp.labelFilter.print();
            }
            if (null != itrExp.brackets && itrExp.brackets.equals(Brackets.RIGHT.oprator)) {
                //itrExp.brackets.print();
                System.out.print(itrExp.brackets);
            }
            if (null != itrExp.labelOperator) {
                //itrExp.labelOperator.print();
                System.out.print(itrExp.labelOperator);
            }
            itrExp = itrExp.expression;
        } while (itrExp != null);
    }

    public String buildStringExpr() {
        LabelExpression itrExp = this;
        StringBuilder expr = new StringBuilder();
        do {
            if (null != itrExp.brackets && itrExp.brackets.equals(Brackets.LEFT.oprator)) {
                expr.append(itrExp.brackets);
            }
            if (null != itrExp.labelFilter) {
                expr.append(itrExp.labelFilter);
            }
            if (null != itrExp.brackets && itrExp.brackets.equals(Brackets.RIGHT.oprator)) {
                expr.append(itrExp.brackets);
            }
            if (null != itrExp.labelOperator) {
                expr.append(itrExp.labelOperator);
            }
            itrExp = itrExp.expression;
        } while (itrExp != null);
        return expr.toString();
    }

    public enum LabelOperator implements Printable {
        AND(" and ", "并且"),
        OR(" or ", "或者"),
        AND_NOT(" and_not ", "且非");

        private String oprator;

        private String desc;

        LabelOperator(String oprator, String desc) {
            this.oprator = oprator;
            this.desc = desc;
        }

        @Override
        public String toString() {
            return oprator;
        }

        @Override
        public void print() {
            System.out.print(" " + this.oprator + " ");
        }

        public static LabelOperator getByOprator(String oprator) {
            LabelOperator labelOperator = null;
            if (AND.toString().equals(oprator)) {
                labelOperator = AND;
            }
            if (OR.toString().equals(oprator)) {
                labelOperator = OR;
            }
            if (AND_NOT.toString().equals(oprator)) {
                labelOperator = AND_NOT;
            }
            return labelOperator;
        }
    }

    public enum Brackets implements Printable {
        LEFT("(", "左括号"),
        RIGHT(")", "右括号");

        private String oprator;

        private String desc;

        Brackets(String oprator, String desc) {
            this.oprator = oprator;
            this.desc = desc;
        }

        @Override
        public String toString() {
            return oprator;
        }

        @Override
        public void print() {
            System.out.print(this.oprator);
        }

        public static Brackets getByOprator(String oprator) {
            Brackets brackets = null;
            if (LEFT.toString().equals(oprator)) {
                brackets = LEFT;
            }
            if (RIGHT.toString().equals(oprator)) {
                brackets = RIGHT;
            }
            return brackets;
        }
    }

    public boolean valid() {
        LabelExpression labelExpression = this;
        int leftBracketsCount = 0;
        int rightBracketsCount = 0;
        int labelCount = 0;
        int operatorCount = 0;
        while (null != labelExpression) {
            if (null != labelExpression.getBrackets() && labelExpression.getBrackets().length() > 0) {
                if (null == Brackets.getByOprator(labelExpression.getBrackets())) {
                    return false;
                } else {
                    if (labelExpression.getBrackets().equals(Brackets.LEFT.oprator)) {
                        ++leftBracketsCount;
                    } else {
                        ++rightBracketsCount;
                    }
                }
            }
            if (null != labelExpression.getLabelFilter()) {
                if (StringUtils.isEmpty(labelExpression.getLabelFilter().getCategoryId()) || StringUtils.isEmpty(labelExpression.getLabelFilter().getLabelId())) {
                    return false;
                }
                ++labelCount;
            }
            if (null != labelExpression.getLabelOperator() && labelExpression.getLabelOperator().length() > 0) {
                if (null == LabelOperator.getByOprator(labelExpression.getLabelOperator())) {
                    return false;
                } else {
                    ++operatorCount;
                }
            }
            labelExpression = labelExpression.expression;
        }
        if (leftBracketsCount != rightBracketsCount) {
            return false;
        }
        if (labelCount - 1 != operatorCount) {
            return false;
        }
        return true;
    }

    public static void main(String[] args) {
        LabelExpression expression = new LabelExpression();
        LabelFilter labelFilter1 = new LabelFilter();
        labelFilter1.setCategoryId("1");
        labelFilter1.setLabelId("2");
        expression.setLabelFilter(labelFilter1);
        LabelExpression expression1 = new LabelExpression();
        LabelFilter labelFilter2 = new LabelFilter();
        labelFilter2.setCategoryId("1");
        labelFilter2.setLabelId("3");
        expression1.setLabelFilter(labelFilter2);
        LabelExpression expression2 = new LabelExpression();
        LabelFilter labelFilter3 = new LabelFilter();
        labelFilter3.setCategoryId("2");
        labelFilter3.setLabelId("3");
        expression2.setLabelFilter(labelFilter3);
        LabelExpression expression3 = new LabelExpression();
        LabelFilter labelFilter4 = new LabelFilter();
        labelFilter4.setCategoryId("3");
        labelFilter4.setLabelId("1");
        expression3.setLabelFilter(labelFilter4);
        LabelExpression expression4 = new LabelExpression();
        LabelFilter labelFilter5 = new LabelFilter();
        labelFilter5.setCategoryId("3");
        labelFilter5.setLabelId("2");
        expression4.setLabelFilter(labelFilter5);
        LabelExpression expression5 = new LabelExpression();
        LabelFilter labelFilter6 = new LabelFilter();
        labelFilter6.setCategoryId("4");
        labelFilter6.setLabelId("1");
        expression5.setLabelFilter(labelFilter6);
        LabelExpression expression44 = new LabelExpression();
        LabelFilter labelFilter44 = new LabelFilter();
        labelFilter44.setCategoryId("5");
        labelFilter44.setLabelId("2");
        expression44.setLabelFilter(labelFilter44);
        LabelExpression expression444 = new LabelExpression();
        LabelFilter labelFilter444 = new LabelFilter();
        labelFilter444.setCategoryId("6");
        labelFilter444.setLabelId("5");
        expression444.setLabelFilter(labelFilter444);
        LabelExpression expression4447 = new LabelExpression();
        LabelFilter labelFilter4447 = new LabelFilter();
        labelFilter4447.setCategoryId("2");
        labelFilter4447.setLabelId("4");
        expression4447.setLabelFilter(labelFilter4447);
        demo();
        System.out.println("----");
//        LabelExpression itrExp2 = new LabelExpression();
        LabelExpression itrExp = getLabelExpression1(expression, expression1, expression44, expression444,expression4447, expression2, expression3);
        System.out.println();
        LabelExpression itrExp2 = getLabelExpression1(expression4);
        System.out.println();
        LabelExpression itrAll = new LabelExpression();
        itrAll.setExpression(itrExp);
        LabelExpression lastExpression = getLastExpression(itrExp);
        lastExpression.setExpression(itrExp2);
        lastExpression.setLabelOperator(LabelOperator.AND_NOT.toString());

        itrAll.print();
        LabelExpression andnotEx =new LabelExpression();
        andnotEx.setExpression(itrExp2);
        andnotEx.setLabelOperator(LabelOperator.AND_NOT.toString());
        System.out.println();
        andnotEx.print();

//        LabelExpression itrExp = getLabelExpression2(expression, expression1, expression2, expression3);
//        itrExp.beginBrackets();
//        itrExp.setExpression(expression);
//        expression.beginBrackets();
//        expression.setLabelOperator(LabelExpression.LabelOperator.OR.toString());
//        expression.setExpression(expression1);
//        expression1.endBrackets();
//        expression2.endBrackets();
//        expression1.setLabelOperator(LabelExpression.LabelOperator.AND.toString());
//        expression1.setExpression(expression2);
//        expression2.setLabelOperator(LabelOperator.AND_NOT.toString());
//        expression2.setExpression(itrExp2);
//        //====
//        itrExp2.beginBrackets();
//        itrExp2.setExpression(expression3);
//        expression3.beginBrackets();
//        expression3.setLabelOperator(LabelOperator.OR.toString());
//        expression3.setExpression(expression4);
//        expression4.endBrackets();
//        expression4.setExpression(expression5);
//        expression4.setLabelOperator(LabelOperator.AND.toString());
//        expression5.endBrackets();
//        itrExp.print();
//        System.out.println();
//        System.out.println(JSON.toJSONString(itrExp));
//        (1:2 or 1:3) and 2:3) or 3:1
//        Stack<String> stack = new Stack<>();
//        List<LabelFilter> labelFilters = extractLabel(itrExp);
//        System.out.println(labelFilters);
    }

    private static void demo() {
        LabelExpression expression = new LabelExpression();
        LabelFilter labelFilter1 = new LabelFilter();
        labelFilter1.setCategoryId("1");
        labelFilter1.setLabelId("2");
        expression.setLabelFilter(labelFilter1);
        LabelExpression expression1 = new LabelExpression();
        LabelFilter labelFilter2 = new LabelFilter();
        labelFilter2.setCategoryId("1");
        labelFilter2.setLabelId("3");
        expression1.setLabelFilter(labelFilter2);
        LabelExpression expression2 = new LabelExpression();
        LabelFilter labelFilter3 = new LabelFilter();
        labelFilter3.setCategoryId("2");
        labelFilter3.setLabelId("3");
        expression2.setLabelFilter(labelFilter3);
        LabelExpression expression3 = new LabelExpression();
        LabelFilter labelFilter4 = new LabelFilter();
        labelFilter4.setCategoryId("3");
        labelFilter4.setLabelId("1");
        expression3.setLabelFilter(labelFilter4);
        LabelExpression itrExp = new LabelExpression();
        itrExp.beginBrackets();
        itrExp.setExpression(expression);
        expression.beginBrackets();
        expression.setLabelOperator(LabelExpression.LabelOperator.OR.toString());
        expression.setExpression(expression1);
        expression1.endBrackets();
//        expression2.endBrackets();
        expression1.setLabelOperator(LabelExpression.LabelOperator.AND.toString());
        expression1.setExpression(expression2);
        expression2.setLabelOperator(LabelOperator.AND.toString());
        expression2.setExpression(expression3);
        expression3.endBrackets();
//        ((1:2 or 1:3) and 2:3) or 3:1
        itrExp.print();
    }

    private static LabelExpression getLastExpression(LabelExpression expression) {
        while (expression.getExpression() != null) {
            expression = expression.getExpression();
        }
        return expression;
    }

    private static LabelExpression getLabelExpression2(LabelExpression expression, LabelExpression expression1, LabelExpression expression2, LabelExpression expression3) {
        List<LabelExpression> expressionList = Lists.newArrayList(expression, expression1, expression2, expression3);
        Map<String, List<LabelExpression>> map = expressionList.stream().collect(Collectors.groupingBy(l -> l.getLabelFilter().getCategoryId()));
        map.forEach((k, v) -> {
            System.out.println(v);
        });
        LabelExpression all = new LabelExpression();
        List<LabelExpression> fenlei = Lists.newArrayList();
        for (Map.Entry<String, List<LabelExpression>> entry : map.entrySet()) {
            String k = entry.getKey();
//            LabelExpression itrExp = new LabelExpression();
//            fenlei.add(itrExp);
//            itrExp.beginBrackets();
            List<LabelExpression> v = entry.getValue();
            LabelExpression itrExp = null;
            for (int i = 0; i < v.size(); i++) {
                LabelExpression le = v.get(i);
                if (i == 0) {
                    itrExp = le;
                    le.beginBrackets();
                }
                if (i < v.size() - 1) {
                    le.setLabelOperator(LabelOperator.OR.toString());
                    le.setExpression(v.get(i + 1));
                }
                if (i == v.size() - 1) {
                    le.endBrackets();
                }
            }
            itrExp.print();
            System.out.println();
        }
//        all.print();
        return all;
    }

    private static LabelExpression getLabelExpression1(LabelExpression... labelExpressions) {
        List<LabelExpression> expressionList = Lists.newArrayList(labelExpressions);
        Map<String, List<LabelExpression>> map = expressionList.stream().collect(Collectors.groupingBy(l -> l.getLabelFilter().getCategoryId()));
        LabelExpression itrExp = new LabelExpression();
        // ((1:2 or 1:3) and 2:3) or 3:1
        LabelExpression before = null;
        int mapsize = map.entrySet().size();
        int n = 0;
        for (Map.Entry<String, List<LabelExpression>> entry : map.entrySet()) {
            ++n;
            String k = entry.getKey();
            List<LabelExpression> v = entry.getValue();
            int size = v.size();
            for (int i = 0; i < v.size(); i++) {
                LabelExpression labelExpression = v.get(i);
                if (n == 1) {
                }
                if (size > 1 && i < size - 1) {
                    labelExpression.beginBrackets();
                    labelExpression.setLabelOperator(LabelOperator.OR.toString());
                }
                if (i == 0) {
                    if (itrExp.getExpression() == null) {
                        itrExp.beginBrackets();
                        itrExp.setExpression(labelExpression);
                    }
                    if (before != null && !before.getLabelFilter().getCategoryId().equals(k)) {
                        before.setExpression(labelExpression);
                    }
                }
                if (i < size - 1) {
                    labelExpression.setExpression(v.get(i + 1));
                }
                if (i == size - 1) {
                    if (n != mapsize) {
                        labelExpression.setLabelOperator(LabelOperator.AND.toString());
                    } else {
                        labelExpression.endBrackets();
                    }
                    before = labelExpression;
                    if (size > 1) {
                        labelExpression.endBrackets();
                    }
                }
            }
        }
        itrExp.print();
        return itrExp;
    }

    private void list(LabelExpression expression) {
        expression.getLabelFilter();
    }

    public static List<LabelFilter> extractLabel(LabelExpression labelExpression) {
        LabelExpression expression = labelExpression;
        List<LabelFilter> labelFilters = Lists.newArrayList();
        while (expression != null) {
            if (null != expression.getLabelFilter()) {
                labelFilters.add(expression.getLabelFilter());
            }
            expression = expression.getExpression();
        }
        return labelFilters;
    }
}

    